/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/

#pragma once

#include <esent.h>
#include <memory>
#include <mutex>
#include <functional>
#include <db/db_interfaces.h>
#include <db/db_table_definition.h>
#include <db/db_table_instance.h>

 namespace test_wrappers { namespace storage {

namespace b = test_wrappers;

typedef std::function<void()> for_each_func;

class db_jet_engine
{
public:
    // instatiation/destruction
    db_jet_engine();

    ~db_jet_engine();

    static std::shared_ptr<db_jet_engine> create();

    void createInstance(
        const std::wstring& workingFolderName
        );

    void beginSession();

    void createDatabase(
        const std::wstring& dbFileName
        );

    void openDatabase(
        const std::wstring& fileName
        );

    // setters
    void setPageSize(
        const unsigned int& size
        );

    void setTempPath(
        const std::wstring& tempPath
        );

    void setLogFilePath(
        const std::wstring& logFilePath
        );

    void setSystemPath(
        const std::wstring& systemPath
        );

    // table instantiation
    table_instance createTable(
        const table_definition& tableDefinition   // not a const because the engine will set the id.
        );

    table_instance instantiateTable(
        const table_definition& tableDefinition   // not a const because the engine will set the id.
        );

    // table operations
    void insert(
        const table_instance& tableInstance,
        const tuple_values& tupleInstance
        );

    void update(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria,
        const tuple_values& newValues
        );

    void read(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria,
        const std::vector<field_id_b>& selectedFields,
        std::vector<tuple_values>& readRows
        );

    void readAll(
        const table_instance& tableInstance,
        const object_id& indexId,
        const std::vector<field_id_b>& selectedFields,
        std::vector<tuple_values>& readRows
        );

    void dispose(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria
        );

    void disposeAll(
        const table_instance& tableInstance,
        const object_id& indexId
        );

private:
    enum operation_type
    {
        insertRecord,
        updateRecord
    };

    enum key_type
    {
        start_key,
        end_key
    };

    // As long as every session is a context in which all operations with databases are performed, 
    // every session controls transaction borders. A transaction may start and finish only within 
    // the limits of a session. An application may create more than one session to a database, possibly 
    // increasing its performance thereby.

    JET_INSTANCE _instance;
    JET_SESID _sessionId;
    JET_DBID _dbID;

    std::recursive_mutex _mutex;

    void shutdown();

    void writeRow(
        const table_instance& tableInstance,
        const tuple_values& tupleValues,
        const operation_type& operationType
        );

    void updateCurrentRow(
        const table_instance& tableInstance,
        const tuple_values& tupleValues
        );

    void readCurrentRow(
        const table_instance& tableInstance,
        const std::vector<field_id_b>& selectedFields,
        std::vector<tuple_values>& readRows
        );

    void deleteCurrentRow(
        const table_instance& tableInstance
        );

    void makeKey(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria,
        const key_type& keyType);

    bool setStartKey(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria
        );

    bool setEndKey(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria
        );

    void forEachMatch(
        const table_instance& tableInstance,
        const select_criteria& selectCriteria,
        for_each_func func
        );

    void forAll(
        const table_instance& tableInstance,
        const object_id& indexId,
        for_each_func func
        );
};

std::shared_ptr<db_jet_engine> createEngine();

}} // namespace s